﻿using Microsoft.Xna.Framework;

namespace xEngine.Maths
{
    public struct Triangle
    {
        #region Member Variables
        
        private Vector3 _normal;
        
        #endregion

        #region Properties

        public Vector3 A { get; set; }
        public Vector3 B { get; set; }
        public Vector3 C { get; set; }
        public Vector3 Normal
        {
            get
            {
                // Need to calculate normal ?
                if (_normal == Vector3.Zero)
                    _normal = Vector3.Normalize(Vector3.Cross(B - A, C - A));

                return _normal;
            }
            set { _normal = value; }
        }

        #endregion

        #region Constructors

        public Triangle(Vector3 a, Vector3 b, Vector3 c) : this()
        {
            _normal = Vector3.Zero;
            A = a;
            B = b;
            C = c;
        }

        #endregion

        #region Functions
        
        public bool ContainsPoint2D(Point p)
        {
            return ContainsPoint2D(Utils.Convert.PointToV2(p));
        }
        public bool ContainsPoint2D(float x, float z)
        {
            return ContainsPoint2D(new Vector2(x, z));
        }
        public bool ContainsPoint2D(Vector3 p)
        {
            return ContainsPoint2D(Utils.Convert.V3ToV2xz(p));
        }
        // Barycentric Technique
        public bool ContainsPoint2D(Vector2 p)
        {
            // Flat points since we are interested in 2D
            Vector2 fA = new Vector2(A.X, A.Z);
            Vector2 fB = new Vector2(B.X, B.Z);
            Vector2 fC = new Vector2(C.X, C.Z);

            // Compute vectors  
            Vector2 vBA = fB - fA;
            Vector2 vCA = fC - fA;
            Vector2 vPA = p - fA;

            // Compute dot products
            float dCACA = Vector2.Dot(vCA, vCA);
            float dCABA = Vector2.Dot(vCA, vBA);
            float dCAPA = Vector2.Dot(vCA, vPA);
            float dBABA = Vector2.Dot(vBA, vBA);
            float dBAPA = Vector2.Dot(vBA, vPA);

            // Compute barycentric coordinates
            float invDenom = 1 / (dCACA * dBABA - dCABA * dCABA);
            float u = (dBABA * dCAPA - dCABA * dBAPA) * invDenom;
            float v = (dCACA * dBAPA - dCABA * dCAPA) * invDenom;

            // Check if point is in triangle
            return (u >= 0) && (v >= 0) && (u + v < 1);
        }

        public float InterpolateY(Point p)
        {
            return InterpolateY(p.X, p.Y);
        }
        public float InterpolateY(Vector2 p)
        {
            return InterpolateY(p.X, p.Y);
        }
        public float InterpolateY(Vector3 p)
        {
            return InterpolateY(p.X, p.Z);
        }
        public float InterpolateY(float x, float z)
        {
            return -(Normal.X * (x - A.X) + Normal.Z * (z - A.Z)) / Normal.Y + A.Y;
        }

        public override string ToString()
        {
            return "Triangle - A = " + A + " | B = " + B + " | C = " + C;
        }

        #endregion
    }
}
